import java.io.Console;

public class 解释器 {

    private byte[] 字节码;
    private int 字节码尺寸;
    private Object[] 常量池;
    private int 当前地址;
    private 函数符号 入口函数;
    private Object[] 全局变量;

    private static final int 默认调用栈大小 = 1000;
    private 栈帧[] 调用栈 = new 栈帧[默认调用栈大小];
    private int 栈顶 = -1; // 栈指针寄存器

    public StringBuffer 输出;

    public boolean 输出执行信息 = true;

    public void 加载(byte[] 字节码, int 字节码尺寸, Object[] 常量池, 函数符号 入口函数, int 数据尺寸) {
        this.字节码 = 字节码;
        this.字节码尺寸 = 字节码尺寸;
        this.常量池 = 常量池;
        this.入口函数 = 入口函数;
        全局变量 = new Object[数据尺寸];
    }

    public void 执行() {
        输出 = new StringBuffer();

        if (输出执行信息) {
            反汇编器 = new 反汇编器(字节码, 字节码尺寸, 常量池);
        }

        当前地址 = 0;

        if (入口函数 == null) {
            入口函数 = new 函数符号("main", 0, 0, 0);
        }

        var 调用帧 = new 栈帧(入口函数, 当前地址);
        栈顶++;
        调用栈[栈顶] = 调用帧;
        当前地址 = 入口函数.地址;
        cpu();
    }

    private void cpu() {
        int i, j, k, 地址, 字段索引;

        var 操作码 = 字节码[当前地址];
        while (操作码 != 字节码定义.指令_HALT && 当前地址 < 字节码尺寸) {
            if (输出执行信息) 跟踪显示();

            当前地址++;
            var 寄存器组 = 调用栈[栈顶].寄存器;
            switch (操作码) {
                case 字节码定义.指令_IADD: // r[k] = r[i] + r[j]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    k = 取寄存器操作数();
                    寄存器组[k] = (Integer) 寄存器组[i] + (Integer) 寄存器组[j];
                    break;

                case 字节码定义.指令_ISUB: // r[k] = r[i] - r[j]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    k = 取寄存器操作数();
                    寄存器组[k] = (Integer) 寄存器组[i] - (Integer) 寄存器组[j];
                    break;

                case 字节码定义.指令_IMUL: // r[k] = r[i] * r[j]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    k = 取寄存器操作数();
                    寄存器组[k] = (Integer) 寄存器组[i] * (Integer) 寄存器组[j];
                    break;

                case 字节码定义.指令_ILT: // r[k] = r[i] < r[j]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    k = 取寄存器操作数();
                    寄存器组[k] = (Integer) 寄存器组[i] < (Integer) 寄存器组[j];
                    break;

                case 字节码定义.指令_IEQ: // r[k] = r[i] == r[j]
                case 字节码定义.指令_FEQ:
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    k = 取寄存器操作数();
                    寄存器组[k] = 寄存器组[i] == 寄存器组[j];
                    break;


                case 字节码定义.指令_FADD: // r[k] = r[i] + r[j]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    k = 取寄存器操作数();
                    寄存器组[k] = (Float) 寄存器组[i] + (Float) 寄存器组[j];
                    break;

                case 字节码定义.指令_FSUB: // r[k] = r[i] - r[j]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    k = 取寄存器操作数();
                    寄存器组[k] = (Float) 寄存器组[i] - (Float) 寄存器组[j];
                    break;

                case 字节码定义.指令_FMUL: // r[k] = r[i] * r[j]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    k = 取寄存器操作数();
                    寄存器组[k] = (Float) 寄存器组[i] * (Float) 寄存器组[j];
                    break;

                case 字节码定义.指令_FLT: // r[k] = r[i] < r[j]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    k = 取寄存器操作数();
                    寄存器组[k] = (Float) 寄存器组[i] < (Float) 寄存器组[j];
                    break;

                case 字节码定义.指令_ITOF: // r[j] = (Float)r[i]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    寄存器组[j] = 寄存器组[i];
                    break;

                case 字节码定义.指令_CALL: // call(函数索引, 寄存器起始索引)
                    int 函数索引 = 取整数操作数();
                    int 寄存器起始索引 = 取寄存器操作数();
                    调用(函数索引, 寄存器起始索引);
                    break;

                case 字节码定义.指令_RET: // ret
                    var 帧 = 调用栈[栈顶];
                    栈顶--;
                    当前地址 = 帧.返回地址;
                    // 拷贝返回值
                    调用栈[栈顶].寄存器[0] = 帧.寄存器[0];
                    break;

                case 字节码定义.指令_BR: // 无条件跳转
                    当前地址 = 取整数操作数();
                    break;

                case 字节码定义.指令_BRT: // true则跳转
                    i = 取寄存器操作数();
                    地址 = 取整数操作数();
                    if ((Boolean) 寄存器组[i]) {
                        当前地址 = 地址;
                    }
                    break;

                case 字节码定义.指令_BRF: // false则跳转
                    i = 取寄存器操作数();
                    地址 = 取整数操作数();
                    if (!(Boolean) 寄存器组[i]) {
                        当前地址 = 地址;
                    }
                    break;

                case 字节码定义.指令_CCONST:
                    i = 取寄存器操作数();
                    j = 取整数操作数();
                    寄存器组[i] = (char) j;
                    break;

                case 字节码定义.指令_ICONST:
                    i = 取寄存器操作数();
                    j = 取整数操作数();
                    寄存器组[i] = j;
                    break;

                case 字节码定义.指令_FCONST:
                case 字节码定义.指令_SCONST:
                    i = 取寄存器操作数();
                    var 常量池索引 = 取整数操作数();
                    寄存器组[i] = 常量池[常量池索引];
                    break;

                case 字节码定义.指令_GLOAD:
                    i = 取寄存器操作数();
                    地址 = 取整数操作数();
                    寄存器组[i] = 全局变量[地址];
                    break;

                case 字节码定义.指令_GSTORE:
                    i = 取寄存器操作数();
                    地址 = 取整数操作数();
                    全局变量[地址] = 寄存器组[i];
                    break;

                case 字节码定义.指令_FLOAD: // r[i] = r[j][n]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    字段索引 = 取整数操作数();
                    寄存器组[i] = ((结构体空间) 寄存器组[j]).字段[字段索引];
                    break;

                case 字节码定义.指令_FSTORE: // r[j][n] = r[i]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    字段索引 = 取整数操作数();
                    ((结构体空间) 寄存器组[j]).字段[字段索引] = 寄存器组[i];
                    break;

                case 字节码定义.指令_MOVE: // r[j] = r[i]
                    i = 取寄存器操作数();
                    j = 取寄存器操作数();
                    寄存器组[j] = 寄存器组[i];
                    break;

                case 字节码定义.指令_PRINT:
                    i = 取寄存器操作数();
                    输出.append(寄存器组[i]);
                    break;

                case 字节码定义.指令_STRUCT:
                    i = 取寄存器操作数();
                    var 字段数 = 取整数操作数();
                    寄存器组[i] = new 结构体空间(字段数);
                    break;

                case 字节码定义.指令_NULL: // r[i] = null
                    i = 取寄存器操作数();
                    寄存器组[i] = null;
                    break;
            }

            操作码 = 字节码[当前地址];
        }

    }

    private int 取寄存器操作数() {
        return 取整数操作数();
    }

    private int 取整数操作数() {
        var 字 = 字节码汇编器.读整数(字节码, 当前地址);
        当前地址 += 4;
        return 字;
    }

    private void 调用(int 函数索引, int 寄存器起始索引) {
        var 函数符号 = (函数符号) 常量池[函数索引];
        var 调用帧 = 调用栈[栈顶];
        var 帧 = new 栈帧(函数符号, 当前地址);
        栈顶++;
        调用栈[栈顶] = 帧;

        // 把参数从调用帧拷贝到新帧
        // 拷贝参数，并且留1个位置给返回值(r0)
        if (函数符号.参数个数 >= 0) {
            System.arraycopy(调用帧.寄存器, 寄存器起始索引, 帧.寄存器, 1, 函数符号.参数个数);
        }

        当前地址 = 函数符号.地址;
    }


    private 反汇编器 反汇编器;

    private void 跟踪显示() {
        反汇编器.反汇编指令(当前地址, true);
        System.out.print(反汇编器.反汇编输出.toString());

        var 寄存器组 = 调用栈[栈顶].寄存器;
        var 当前帧 = 调用栈[栈顶];

        // 栈顶
        System.out.print("\t" + 当前帧.函数符号.名称 + ".寄存器组=[ ");

        // 返回值        
        if (寄存器组.length >= 1) {
            System.out.print(取寄存器信息(寄存器组[0]));
        }

        // 参数列表
        if (寄存器组.length >= 2 && 当前帧.函数符号.参数个数 > 0) {
            System.out.print(" |");
            for (int i = 1; i <= 当前帧.函数符号.参数个数; i++) {
                System.out.print(" " + 取寄存器信息(寄存器组[i]));
            }
        }

        // 局部变量
        if (寄存器组.length >= 2 && 当前帧.函数符号.局部变量个数 > 0) {
            System.out.print(" |");
            for (int i = 1 + 当前帧.函数符号.参数个数; i < 寄存器组.length; i++) {
                System.out.print(" " + 取寄存器信息(寄存器组[i]));
            }
        }

        System.out.print(" ]");


        // 调用栈
        if (栈顶 >= 0) {
            System.out.print("  调用栈=[");
            for (int i = 0; i <= 栈顶; i++) {
                System.out.print(" " + 调用栈[i].函数符号.名称);
            }

            System.out.print(" ]");
        }

        System.out.print("\n");
    }

    private static String 取寄存器信息(Object 寄存器值) {
        if (寄存器值 == null) {
            return "?";
        } else {
            return 寄存器值.toString();
        }
    }
}
